package com.ruoyi.framework.security.handle;

import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.config.OAuth2CacheManager;
import com.ruoyi.framework.config.OAuth2SecurityConfig;
import com.ruoyi.framework.config.properties.KeycloakConfig;
import com.ruoyi.framework.web.service.SysPermissionService;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.service.ISysUserService;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

@Component
@Slf4j
public class OAuth2LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    @Resource
    private KeycloakConfig keycloakConfig;

    @Resource
    private OAuth2CacheManager oauth2CacheManager;

    @Resource
    private TokenService tokenService;

    @Resource
    private ISysUserService userService;

    @Value("${oauth2.def_password}")
    private String defPassword;

    @Value("${oauth2.def_roles}")
    private String defRoles;

    @Resource
    private SysPermissionService permissionService;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
            throws IOException, ServletException {

        if (authentication instanceof OAuth2AuthenticationToken oauth2Token) {
            OidcUser oidcUser = (OidcUser) authentication.getPrincipal();
            String idToken = oidcUser.getIdToken().getTokenValue();
            String sid = oidcUser.getAttribute("sid");
            oauth2CacheManager.saveIdToken(request, sid, idToken);
            oauth2CacheManager.saveAccessTokenExpiresAt(request, sid, oidcUser.getExpiresAt());

            OAuth2SecurityConfig.sessionMap.put(sid, request.getSession().getId()); // 将sid与sessionId的映射关系存储起来，单点注销时会用到
            OidcUserInfo userInfo = oidcUser.getUserInfo();
            LoginUser loginUser = getLoginUser(userInfo);
            if(loginUser != null){
                String token = tokenService.createToken(loginUser);
                OAuth2SecurityConfig.tokenMap.put(sid, token); // 将sid与token的映射关系存储起来，单点注销时会用到
                String targetUrl = keycloakConfig.getFrontUrl();
                // 登录成功并获取到用户信息则重定向到前端的 oauth2Login 路由
                targetUrl += "/oauth2Login?oauth2LoginToken=" + URLEncoder.encode(token, StandardCharsets.UTF_8);
                targetUrl += "&thirdType=" + oauth2Token.getAuthorizedClientRegistrationId();
                getRedirectStrategy().sendRedirect(request, response, targetUrl);
            }
        } else {
            super.onAuthenticationSuccess(request, response, authentication);
        }
    }

    public LoginUser getLoginUser(OidcUserInfo userInfo){
        String username = userInfo.getPreferredUsername();
        SysUser sysUser = userService.selectUserByUserName(username);
        if(sysUser == null){
            // 用户不存在则创建
            sysUser = new SysUser();
            sysUser.setUserName(username);
            sysUser.setEmail(userInfo.getEmail());
            sysUser.setAvatar(userInfo.getPicture());
            sysUser.setPhonenumber(userInfo.getPhoneNumber());
            sysUser.setSex(userInfo.getGender() == null ? "0" : (userInfo.getGender().equals("male") ? "1" : "2"));
            sysUser.setNickName(getFullName(userInfo.getFamilyName(), userInfo.getGivenName()));
            sysUser.setPassword(SecurityUtils.encryptPassword(defPassword));
            sysUser.setStatus("0");
            sysUser.setDelFlag("0");
            if(StringUtils.isNotBlank(defRoles)){
                String[] roles = defRoles.split(",");
                List<Long> roleIdList = new ArrayList<>();
                for(String roleId : roles){
                    try{
                        roleIdList.add(Long.parseLong(roleId));
                    }catch (Exception e){
                        log.error(e.getMessage());
                    }
                }
                if(!roleIdList.isEmpty()){
                    Long[] roleIds = new Long[roleIdList.size()];
                    for(int i=0;i<roleIdList.size();i++){
                        roleIds[i] = roleIdList.get(i);
                    }
                    sysUser.setRoleIds(roleIds);
                }

            }
            int i = userService.insertUser(sysUser);
            if(i > 0){
                sysUser = userService.selectUserByUserName(username);
            }
        }else{
            if (UserStatus.DELETED.getCode().equals(sysUser.getDelFlag())){
                log.info("登录用户：{} 已被删除.", username);
                throw new ServiceException(MessageUtils.message("user.password.delete"));
            }
            else if (UserStatus.DISABLE.getCode().equals(sysUser.getStatus())){
                log.info("登录用户：{} 已被停用.", username);
                throw new ServiceException(MessageUtils.message("user.blocked"));
            }
        }
        return new LoginUser(sysUser.getUserId(), sysUser.getDeptId(), sysUser, permissionService.getMenuPermission(sysUser));
    }

    private String getFullName(String familyName, String givenName) {
        if (StringUtils.isNotBlank(familyName) || StringUtils.isNotBlank(givenName)) {
            if(StringUtils.isBlank(familyName)){
                return givenName;
            }else if(StringUtils.isBlank(givenName)){
                return familyName;
            }else{
                return familyName + givenName;
            }
        }
        return null;
    }
}